package com.hui.stock.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.io.BaseEncoding;
import com.hui.stock.constant.StockConstant;
import com.hui.stock.mapper.*;
import com.hui.stock.pojo.domain.MenuDomain;
import com.hui.stock.pojo.domain.RolesInfoDomain;
import com.hui.stock.pojo.domain.UserInfoDomain;
import com.hui.stock.pojo.domain.UserPageInfoDomain;
import com.hui.stock.pojo.entity.SysPermission;
import com.hui.stock.pojo.entity.SysRole;
import com.hui.stock.pojo.entity.SysUser;
import com.hui.stock.pojo.entity.SysUserRole;
import com.hui.stock.service.UserService;
import com.hui.stock.utils.IdWorker;
import com.hui.stock.utils.UserRolePermissionUtil;
import com.hui.stock.vo.req.AddUserReqVo;
import com.hui.stock.vo.req.LoginReqVo;
import com.hui.stock.vo.req.UpdateUserRoleReqVo;
import com.hui.stock.vo.req.UserPageInfoReqVo;
import com.hui.stock.vo.resp.LoginRespVo;
import com.hui.stock.vo.resp.PageResult;
import com.hui.stock.vo.resp.R;
import com.hui.stock.vo.resp.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpSession;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service("userService")
@Slf4j//编译期增强，自动创建log对象
public class UserServiceImpl implements UserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysRolePermissionMapper sysRolePermissionMapper;

    @Autowired
    private SysPermissionMapper sysPermissionMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 根据用户名查询用户
     * @param userName
     * @return
     */
    @Override
    public SysUser getUserByUserName(String userName) {
        SysUser sysUser = sysUserMapper.findUserInfoByUserName(userName);
        return sysUser;
    }

    @Override
    public R<LoginRespVo> login(LoginReqVo vo) {
        //1 检查参数是否合法
        if (vo == null || StringUtils.isAnyBlank(vo.getUsername(), vo.getPassword(), vo.getCode(), vo.getSessionId())) {
            return R.error(ResponseCode.DATA_ERROR);
        }
        //1.1 检查验证码是否合法
        if (StringUtils.isBlank(vo.getCode())||StringUtils.isBlank(vo.getSessionId())){
            return R.error(ResponseCode.DATA_ERROR);
        }
        //2.1  检查验证码是否输入正确
        // 根据sessionID获取redis中的验证码
        String codeKey = StockConstant.CHECK_PREFIX + vo.getSessionId();
        String storedCode = (String) redisTemplate.opsForValue().get(codeKey);
        if (StringUtils.isBlank(storedCode)){
            return R.error(ResponseCode.CHECK_CODE_TIMEOUT);
        }
        if (!storedCode.equals(vo.getCode())){
            return R.error(ResponseCode.CHECK_CODE_ERROR);
        }
        //2.2 调用mapper方法获取用户
        SysUser user = sysUserMapper.findUserInfoByUserName(vo.getUsername());
        //3 判断用户是否存在,以及密码是否匹配
        if (user==null||!passwordEncoder.matches(vo.getPassword(),user.getPassword())){
            return R.error(ResponseCode.USERNAME_OR_PASSWORD_ERROR);
        }
        //4 封装返回数据
        LoginRespVo loginRespVo = new LoginRespVo();
        // // loginRespVo.setUsername(user.getUsername());
        // // loginRespVo.setNickName(user.getNickName());
        // // loginRespVo.setPhone(user.getPhone());
        // // loginRespVo.setId(user.getId());
        // //上面是传统的方法，spring提供了简单的方法
        // //要求：属性名称和类型必须相同，否则无法copy
        BeanUtils.copyProperties(user,loginRespVo);

        // 完善用户登录成功动态回显菜单栏功能：5. 动态生成菜单栏和权限信息
        //1 获取用户角色id
        List<Long> roleIds = sysRoleMapper.getRoleIds(user.getId());
        if (CollectionUtils.isEmpty(roleIds)){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        //2 根据用户角色id获取菜单列表
        List<SysPermission> permissions = sysRolePermissionMapper.getPermissionList(roleIds);
        if (CollectionUtils.isEmpty(permissions)){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        //按钮
        List<String> buttonPermissions = new ArrayList<>();
        // 分类菜单和按钮权限
        permissions.forEach(permission->{
            if (permission.getType()==3){
                if (!StringUtils.isBlank(permission.getCode())){
                    buttonPermissions.add(permission.getCode());
                }
            }
        });
        loginRespVo.setMenus(UserRolePermissionUtil.buildPermissionTree(permissions));
        loginRespVo.setPermissions(buttonPermissions);
        String info=user.getId()+user.getUsername();
        //后期使用JWT
        String accessToken = BaseEncoding.base64().encode(info.getBytes());
        loginRespVo.setAccessToken(accessToken);
        return R.ok(loginRespVo);
    }

    @Override
    public R<Map> getCaptchaCode() {
        //1 获取设置验证码的对象 参数：宽 高 验证码长度 干扰线数量
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(205, 40, 4, 5);
        //2 设置背景色
        captcha.setBackground(Color.LIGHT_GRAY);
        //3 获取校验码
        String captchaCode = captcha.getCode();
        //4 获取校验码base64格式的图片资源
        String imageBase64 = captcha.getImageBase64();
        //5 生成sessionId
        String sessionId = String.valueOf(idWorker.nextId());
        log.info("当前生成的验证码:{},会话id:{}",captchaCode,sessionId);
        //6 将sessionId和校验码保存到redis中，模拟cookie-sessionId会话方式,sessionId前面增加CK，方面查询
        redisTemplate.opsForValue().set(StockConstant.CHECK_PREFIX + sessionId,captchaCode,5, TimeUnit.MINUTES);
        //7 组装响应数据
        Map<String,String> data = new HashMap<>();
        data.put("sessionId",sessionId);
        data.put("imageData",imageBase64);
        //8 返回响应数据
        return R.ok(data);
    }

    /**
     * 多条件综合查询用户分页信息
     * @param userPageInfoReqVo 查询用户分页信息的请求参数
     * @return
     */
    @Override
    public R<PageResult> queryUserPageInfo(UserPageInfoReqVo userPageInfoReqVo) {
        //1 请求页数和页面尺寸
        Integer pageNum = userPageInfoReqVo.getPageNum();
        Integer pageSize = userPageInfoReqVo.getPageSize();
        //2 启动分页
        PageHelper.startPage(pageNum,pageSize);
        //3 调用mapper返回数据
        List<UserPageInfoDomain> userPageInfo = sysUserMapper.queryUserPageInfo(userPageInfoReqVo.getStartTime(),userPageInfoReqVo.getEndTime());
        //4 封装分页数据
        PageInfo<UserPageInfoDomain> pageInfo = new PageInfo<>(userPageInfo);
        PageResult<UserPageInfoDomain> pageResult = new PageResult<>(pageInfo);
        return R.ok(pageResult);
    }

    @Override
    public R addUser(AddUserReqVo addUserReqVo, HttpSession session) {
        //1 先查询用户名是否唯一
        String username = addUserReqVo.getUsername();
        if (sysUserMapper.findUserInfoByUserName(username)!=null){
            return R.error(ResponseCode.ACCOUNT_EXISTS_ERROR);
        }
        //2 获取登录用户id
        Long loginId = (Long) session.getAttribute("userId");
        //3 封装用户对象
        SysUser newUser = new SysUser();
        //密码加密
        String password = addUserReqVo.getPassword();
        String encode = passwordEncoder.encode(password);
        addUserReqVo.setPassword(encode);
        BeanUtils.copyProperties(addUserReqVo,newUser);
        newUser.setCreateId(loginId);
        newUser.setId(idWorker.nextId());
        newUser.setCreateTime(new Date());
        //4 保存用户对象
        sysUserMapper.insertUser(newUser);
        return R.ok("操作成功");
    }

    @Override
    public R<RolesInfoDomain> getRolesByUserId(Long userId) {
        //1 调用mapper获取角色id
        List<Long> ownRoleIds = sysRoleMapper.getRoleIds(userId);
        //2 调用mapper获取所有角色
        List<SysRole> allRole = sysRoleMapper.getAllRoles();
        //3 封装返回数据
        RolesInfoDomain rolesInfoDomain = new RolesInfoDomain(ownRoleIds,allRole);
        return R.ok(rolesInfoDomain);
    }

    @Override
    public R updateUserRoles(UpdateUserRoleReqVo updateUserRoleReqVo) {
        //1 先删除
        Long userId = updateUserRoleReqVo.getUserId();
        sysUserRoleMapper.deleteByUserId(userId);
        //2 再增加
        List<Long> roleIds = updateUserRoleReqVo.getRoleIds();
        if (!CollectionUtils.isEmpty(roleIds)){
            List<SysUserRole> sysUserRoles = new ArrayList<>();
            for (Long roleId : roleIds) {
                SysUserRole userRole = SysUserRole.builder()
                        .id(idWorker.nextId())
                        .userId(userId)
                        .roleId(roleId)
                        .createTime(new Date())
                        .build();
                sysUserRoles.add(userRole);
            }
            // 批量插入
            sysUserRoleMapper.batchInsert(sysUserRoles);
        }
        return R.ok("操作成功！");
    }

    @Override
    public R deleteUserByUserID(List<Long> userIds) {
        sysUserMapper.batchDeleteByPrimaryKey(userIds);
        return R.ok("操作成功！");
    }

    @Override
    public R<UserInfoDomain> getUserInfoByUserId(Long userId) {
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(userId);
        UserInfoDomain userInfoDomain = new UserInfoDomain();
        BeanUtils.copyProperties(sysUser,userInfoDomain);
        return R.ok(userInfoDomain);
    }

    @Override
    public R updateUserInfo(SysUser sysUser, HttpSession session) {
        sysUser.setUpdateId((Long) session.getAttribute("userId"));
        sysUser.setUpdateTime(new Date());
        SysUser user = sysUserMapper.selectByPrimaryKey(sysUser.getId());
        BeanUtils.copyProperties(sysUser,user,"id","password", "createTime", "createId","status","deleted");
        sysUserMapper.updateByPrimaryKey(user);
        return R.ok("操作成功！");
    }



}
